#include "../Headers/program_cl.h"

/*
static cluster* BaseObject_cl;
static cluster* Program_cl;
*/

/*
static cluster* BaseClass_cl;  // Inherits from BaseObject
static cluster* Module_cl;  // Inherits from BaseObject
static cluster* Builtins_cl;  // Inherits from Module

static cluster* BaseThread_cl;  // Inherits from BaseClass_cl
static cluster* ProcessingControl_cl;  // Inherits from BaseThread_cl

static cluster* BaseNum_cl;  // Inherits from BaseClass
static cluster* Integer_cl;  // Inherits from BaseNum

static cluster* String_cl;  // Inherits from BaseClass

static cluster* BaseInterface_cl;  // Inherits from BaseObject
static cluster* FileStream_cl;  // Inherits from BaseInterface

static cluster* BaseFunction_cl;  // Inherits from BaseObject
static cluster* If_cl;  // Inherits from BaseFunction
static cluster* Elif_cl;  // Inherits from BaseFunction
static cluster* Else_cl;  // Inherits from BaseFunction
static cluster* For_cl;  // Inherits from BaseFunction
static cluster* While_cl;  // Inherits from BaseFunction
static cluster* True_p_cl;  // Inherits from BaseFunction
static cluster* False_p_cl;  // Inherits from BaseFunction
static cluster* And_cl;  // Inherits from BaseFunction
static cluster* Or_cl;  // Inherits from BaseFunction
static cluster* Assign_cl;  // Inherits from BaseFunction
static cluster* TestEq_cl;  // Inherits from BaseFunction
*/

/*!
 * @param Program_cl_
 *  This is assumed to be Program_cl
 */
cluster*
Program_cl_invoke_func(cluster* Program_cl) {
    DEBUG("> Program_cl_invoke_func()\n");

    cl_set_create_time(Program_cl, TIME_INITIAL);  // This is a misnomer. The create time is
                                                   // relative to the containing scope, but the program
                                                   // has no containing scope. Regardless, it's initialized.

    //cl_process_jobs(Program_cl);

    DEBUG("< Program_cl_invoke_func()\n");
    return Program_cl;
}

/*!
 * @param Program_cl_
 *  This is assumed to be Program_cl
 */
void
Program_cl_destroy_func(cluster* Program_cl) {
    DEBUG("> Program_cl_destroy_func()\n");
    DEBUG("***************************************************************\n");
    DEBUG("< Program_cl_destroy_func()\n");
}

/*!
 * The program cluster is the only one that cannot be created via the
 * cl_new function. This is because the program is the primordial container.
 *
 * At the moment, I'm creating a mock program here. A grammar will need to be
 * built to automate this. However, this script will work through the following
 * steps:
 *
 * * Create some basic objects. The basic objects that I'm using are:
 *   * BaseObject_cl
 *   * Program_cl
 *   * GarbageCollector_cl
 *   * DummyPrint_cl (This is a printing interface object)
 *   * DummyInt_cl (Wraps an integer... probably 42)
 *   * DummyStrFunc_cl (Is a function in the DummyInt_cl attributes... there will probably be
 *       Object_cl later on, but I'll worry about that later)
 *   * DummyVar_cl (References the Int)
 *   * DummyModule_cl (This is a standing for the main module).
 *
 * The general program that this is trying to mock up is this:
 * ------
 * $x = 42
 * $print($x)
 * ------
 */
void
kickstart_Program_cl(int argc, char** argv) {
    cluster *BaseObject_cl, *Program_cl, *GarbageCollector_cl;
    cluster *DummyPrint_cl, *DummyStrFunc_cl, *DummyInt_cl, *DummyVar_cl, *DummyModule_cl;
    /*! @TODO manually initialize all of the other attributes that need to
     *  be initialized. Fore example, program (should) inherit from BaseObject
     *  in some way.
     *  @TODO All of these tasks could be appended to the jobs list. Then, when
     *  Program_cl is invoked, it will work through its jobs.
     */
    DEBUG("> initialize_program()\n");

    GarbageCollector_cl = initialize_registry();

    // BaseObject is the ancestor of everything, including Program, so it should
    // be created first.
    BaseObject_cl = cl_new();  // This looks odd. Maybe it should be cl_new(cluster*)?
                               // Along the same vein, the variable declarations at the top
                               // of functions that need them could be done via a macro.
    cl_set_name(BaseObject_cl, "BaseObject");
    cl_register(BaseObject_cl);

    // Initialize program
    Program_cl = cl_new();
    cl_set_name(Program_cl, "Program");
    cl_append(&(Program_cl->parents), BaseObject_cl);
    cl_set_invoke_func(Program_cl, &Program_cl_invoke_func);
    cl_set_destroy_func(Program_cl, &Program_cl_destroy_func);
    cl_register(Program_cl);

    // Set up the dummy main module
    DummyModule_cl = cl_new();
    cl_set_name(DummyModule_cl, "DummyModule");
    cl_append(&(DummyModule_cl->parents), BaseObject_cl);
    cl_set_invoke_func(DummyModule_cl, &DummyModule_cl_invoke_func);


    // Set up the DummyStrFunc_cl
    DummyStrFunc_cl = cl_new();
    // The dependency is param

    // Set up the DummyInt_cl


    // Set up the print function
    DummyInt_cl = cl_new();
    cl_set_name(DummyInt_cl, "DummyInt");
    cl_append(&(DummyInt_cl->parents), BaseObject_cl);
    cl_append(&(DummyInt_cl->attributes), DummyStrFunc_cl);  // This will allow the cluster to be printed

    DummyPrint_cl = cl_new();
    cl_set_name(DummyPrint_cl, "DummyPrint");
    cl_set_invoke_func(DummyPrint_cl, &DummyPrint_cl_invoke_func);
    cl_append(&DummyPrint_cl->parents, BaseObject_cl);

    // This is akin to saying:
    // $a = "DummyInt", except the actual string isn't really coded yet, so I'll use the symbol
    // as a standin.
    DummyVar_cl = cl_new();
    cl_set_name(DummyInt_cl, "DummyVar");
    cl_append(&(DummyVar_cl->parents), BaseObject_cl);
    cl_set_identity(DummyVar_cl, 1, DummyInt_cl);  // 1 for line 1

    // Set the print as an attribute of the main module
    cl_append(&DummyModule_cl->attributes, DummyPrint_cl);

    // Initialize GarbageCollector_cl
    cl_set_name(GarbageCollector_cl, "GarbageCollector");
    cl_append(&GarbageCollector_cl->parents, BaseObject_cl);
    //cl_append(GarbageCollector_cl, &Program_cl->jobs);
    cl_append(&Program_cl->attributes, GarbageCollector_cl);
    cl_set_invoke_func(GarbageCollector_cl, &GarbageCollector_cl_invoke_func);

    // Initialize MainModule_cl?

    // Invoke program.
    info_dump(Program_cl);
    //info_dump_registry();
    cl_invoke(Program_cl);
    cl_invoke_destroy(Program_cl);

    DEBUG("< initialize_program()\n");
}
